package cn.kivensoft.xlsx.writer;

import java.security.SecureRandom;
import java.util.Comparator;

public class Style implements Comparator<Style>, Comparable<Style> {
	public Font currentFont;
	public Fill currentFill;
	public Border currentBorder;
	public NumberFormat currentNumberFormat;
	public CellXf currentCellXf;
	public int internalID;

	private String name;

	public String getName() {
		return name;
	}

	public Style() {
		init();
	}

	public Style(String name) {
		init();
		this.name = name;
	}
	
	private void init() {
		this.currentFont = new Font();
		this.currentFill = new Fill();
		this.currentBorder = new Border();
		this.currentNumberFormat = new NumberFormat();
		this.currentCellXf = new CellXf();
		this.name = createUniqueName();
	}

	private String createUniqueName() {
		SecureRandom random = new SecureRandom();
		return String.format("Style%d-%d",
				random.nextInt(Integer.MAX_VALUE),
				random.nextInt(Integer.MAX_VALUE));
	}

	public static class CellXf implements Comparator<CellXf>, Comparable<CellXf> {
		public enum HorizontalAlignValue {
			left, center, right, fill, justify, general, 
			centerContinuous, distributed, none
		}

		public enum VerticallAlignValue {
			bottom, top, center, justify, distributed, none
		}

		public enum TextBreakValue {
			wrapText, shrinkToFit, none
		}

		public enum TextDirectionValue {
			horizontal, vertical
		}

		public boolean locked;
		public boolean hidden;
		public HorizontalAlignValue horizontalAlign;
		public VerticallAlignValue verticalAlign;
		public TextBreakValue alignment;
		public int internalID;
		public int textRotation;
		public TextDirectionValue textDirection;
		public boolean forceApplyAlignment;

		public CellXf() {
			horizontalAlign = HorizontalAlignValue.none;
			alignment = TextBreakValue.none;
			textDirection = TextDirectionValue.horizontal;
			verticalAlign = VerticallAlignValue.none;
			textRotation = 0;
		}

		public int calculateInternalRotation() {
			if (textRotation < -90 || textRotation > 90)
				throw new RuntimeException("The rotation value (" + textRotation
						+ "°) is out of range. Range is form -90° to +90°");

			if (textDirection == TextDirectionValue.vertical) return 255;
			else return textRotation >= 0 ? textRotation : 90 - textRotation;
		}

		public boolean equals(CellXf other) {
			return other != null
					&& horizontalAlign == other.horizontalAlign
					&& alignment == other.alignment
					&& textDirection == other.textDirection
					&& textRotation == other.textRotation
					&& verticalAlign == other.verticalAlign
					&& forceApplyAlignment == other.forceApplyAlignment
					&& locked == other.locked && hidden == other.hidden;
		}

		public CellXf copy() {
			CellXf ret = new CellXf();
			ret.horizontalAlign = horizontalAlign;
			ret.alignment = alignment;
			ret.textDirection = textDirection;
			ret.textRotation = textRotation;
			ret.verticalAlign = verticalAlign;
			ret.forceApplyAlignment = forceApplyAlignment;
			ret.locked = locked;
			ret.hidden = hidden;
			return ret;
		}

		@Override
		public int compare(CellXf o1, CellXf o2) {
			if (o1 == null) return o2 == null ? 0 : -1;
			else if (o2 == null) return 1;
			else if (o1.internalID == o2.internalID) return 0;
			else if (o1.internalID > o2.internalID) return 1;
			else return -1;
		}

		@Override
		public int compareTo(CellXf o) {
			if (o == null) return 1;
			else if (internalID == o.internalID) return 0;
			else if (internalID > o.internalID) return 1;
			else return -1;
		}

	}

	public static class Font implements Comparator<Font>, Comparable<Font> {
		public static final String DEFAULTFONT = "宋体";

		public enum VerticalAlignValue {
			subscript, superscript, none
		}

		public enum SchemeValue {
			major, minor, none
		}

		public int size;
		public String name;
		public String family;
		public int colorTheme;
		public String colorValue;
		public SchemeValue scheme;
		public VerticalAlignValue verticalAlign;
		public boolean bold;
		public boolean italic;
		public boolean underline;
		public boolean doubleUnderline;
		public boolean strike;
		public String charset;
		public int internalID;

		public boolean isDefaultFont() {
			return equals(new Font());
		}

		public Font() {
			size = 9;
			name = DEFAULTFONT;
			family = "2";
			colorTheme = 1;
			colorValue = "";
			charset = "";
			scheme = SchemeValue.minor;
			verticalAlign = VerticalAlignValue.none;
		}

		public boolean equals(Font other) {
			return other != null && bold == other.bold
					&& colorTheme == other.colorTheme
					&& doubleUnderline == other.doubleUnderline
					&& family == other.family && italic == other.italic
					&& name == other.name && scheme == other.scheme
					&& verticalAlign == other.verticalAlign
					&& charset == other.charset && size == other.size
					&& strike == other.strike
					&& underline == other.underline;
		}

		public Font copy() {
			Font ret = new Font();
			ret.bold = bold;
			ret.charset = charset;
			ret.colorTheme = colorTheme;
			ret.verticalAlign = verticalAlign;
			ret.doubleUnderline = doubleUnderline;
			ret.family = family;
			ret.italic = italic;
			ret.name = name;
			ret.scheme = scheme;
			ret.size = size;
			ret.strike = strike;
			ret.underline = underline;
			return ret;
		}

		@Override
		public int compare(Font o1, Font o2) {
			if (o1 == null) return o2 == null ? 0 : -1;
			else if (o2 == null) return 1;
			else if (o1.internalID > o2.internalID) return 1;
			else if (o1.internalID == o2.internalID) return 0;
			else return -1;
		}

		@Override
		public int compareTo(Font o) {
			if (o == null) return 1;
			else if (internalID == o.internalID) return 0;
			else if (internalID > o.internalID) return 1;
			else return -1;
		}

	}

	public static class Fill implements Comparator<Fill>, Comparable<Fill> {
		public static final String DEFAULTCOLOR = "FF000000";

		public enum PatternValue {
			none, solid, darkGray, mediumGray, lightGray, gray0625, gray125
		}

		public enum FillType {
			patternColor, fillColor
		}

		public static String getPatternName(PatternValue pattern) {
			String output = "";
			switch (pattern) {
			case none: output = "none"; break;
			case solid: output = "solid"; break;
			case darkGray: output = "darkGray"; break;
			case mediumGray: output = "mediumGray"; break;
			case lightGray: output = "lightGray"; break;
			case gray0625: output = "gray0625"; break;
			case gray125: output = "gray125"; break;
			default: output = "none"; break;

			}
			return output;
		}

		public int indexedColor;
		public PatternValue patternFill;
		public String foregroundColor;
		public String backgroundColor;
		public int internalID;

		public Fill() {
			indexedColor = 64;
			patternFill = PatternValue.none;
			foregroundColor = DEFAULTCOLOR;
			backgroundColor = DEFAULTCOLOR;
		}

		public Fill(String forground, String background) {
			backgroundColor = background;
			foregroundColor = forground;
			indexedColor = 64;
			patternFill = PatternValue.solid;
		}

		public Fill(String value, FillType filltype) {
			if (filltype == FillType.fillColor) {
				backgroundColor = value;
				foregroundColor = DEFAULTCOLOR;
			} else {
				backgroundColor = DEFAULTCOLOR;
				foregroundColor = value;
			}
			indexedColor = 64;
			patternFill = PatternValue.solid;
		}

		public void setColor(String value, FillType filltype) {
			if (filltype == FillType.fillColor) {
				foregroundColor = value;
				backgroundColor = DEFAULTCOLOR;
			} else {
				foregroundColor = DEFAULTCOLOR;
				backgroundColor = value;
			}
			patternFill = PatternValue.solid;
		}

		public boolean equals(Fill other) {
			return other != null && indexedColor == other.indexedColor
					&& patternFill == other.patternFill
					&& foregroundColor == other.foregroundColor
					&& backgroundColor == other.backgroundColor;
		}

		public Fill copy() {
			Fill ret = new Fill();
			ret.backgroundColor = backgroundColor;
			ret.foregroundColor = foregroundColor;
			ret.indexedColor = indexedColor;
			ret.patternFill = patternFill;
			return ret;
		}

		@Override
		public int compare(Fill o1, Fill o2) {
			if (o1 == null) return o2 == null ? 0 : -1;
			else if (o2 == null) return 1;
			else if (o1.internalID > o2.internalID) return 1;
			else if (o1.internalID == o2.internalID) return 0;
			else return -1;
		}

		@Override
		public int compareTo(Fill o) {
			if (o == null) return 1;
			else if (internalID == o.internalID) return 0;
			else if (internalID > o.internalID) return 1;
			else return -1;
		}

	}

	public static class Border implements Comparator<Border>, Comparable<Border> {
		public enum StyleValue {
			none, hair, dotted, dashDotDot, dashDot, dashed, 
			thin, mediumDashDotDot, slantDashDot, mediumDashDot, 
			mediumDashed, medium, thick, s_double
		}

		public static String getStyleName(StyleValue style) {
			String output = "";
			switch (style) {
				case none:
				case hair:
					break;
				case dotted: output = "dotted"; break;
				case dashDotDot: output = "dashDotDot"; break;
				case dashDot: output = "dashDot"; break;
				case dashed: output = "dashed"; break;
				case thin: output = "thin"; break;
				case mediumDashDotDot: output = "mediumDashDotDot"; break;
				case slantDashDot: output = "slantDashDot"; break;
				case mediumDashDot: output = "mediumDashDot"; break;
				case mediumDashed: output = "mediumDashed"; break;
				case medium: output = "medium"; break;
				case thick: output = "thick"; break;
				case s_double: output = "double"; break;
			}
			return output;
		}

		public StyleValue leftStyle;
		public StyleValue rightStyle;
		public StyleValue topStyle;
		public StyleValue bottomStyle;
		public StyleValue diagonalStyle;
		public boolean diagonalDown;
		public boolean diagonalUp;
		public String leftColor;
		public String rightColor;
		public String topColor;
		public String bottomColor;
		public String diagonalColor;
		public int internalID;

		public Border() {
			bottomColor = "";
			topColor = "";
			leftColor = "";
			rightColor = "";
			diagonalColor = "";
			leftStyle = StyleValue.none;
			rightStyle = StyleValue.none;
			topStyle = StyleValue.none;
			bottomStyle = StyleValue.none;
			diagonalStyle = StyleValue.none;
			diagonalDown = false;
			diagonalUp = false;
		}

		public boolean equals(Border other) {
			return other != null && bottomColor == other.bottomColor
					&& bottomStyle == other.bottomStyle
					&& diagonalColor == other.diagonalColor
					&& diagonalDown == other.diagonalDown
					&& diagonalStyle == other.diagonalStyle
					&& diagonalUp == other.diagonalUp
					&& leftColor == other.leftColor
					&& leftStyle == other.leftStyle
					&& rightColor == other.rightColor
					&& rightStyle == other.rightStyle
					&& topColor == other.topColor
					&& topStyle == other.topStyle;
		}

		public boolean isEmpty() {
			return bottomColor.isEmpty() && topColor.isEmpty()
					&& leftColor.isEmpty() && rightColor.isEmpty()
					&& diagonalColor.isEmpty()
					&& leftStyle == StyleValue.none
					&& rightStyle == StyleValue.none
					&& topStyle == StyleValue.none
					&& bottomStyle == StyleValue.none
					&& diagonalStyle == StyleValue.none
					&& diagonalDown == false && diagonalUp == false;
		}

		public Border copy() {
			Border ret = new Border();
			ret.diagonalColor = diagonalColor;
			ret.diagonalDown = diagonalDown;
			ret.diagonalStyle = diagonalStyle;
			ret.diagonalUp = diagonalUp;
			ret.leftColor = leftColor;
			ret.leftStyle = leftStyle;
			ret.rightColor = rightColor;
			ret.rightStyle = rightStyle;
			ret.topColor = topColor;
			ret.topStyle = topStyle;
			ret.bottomColor = bottomColor;
			ret.bottomStyle = bottomStyle;
			return ret;
		}

		@Override
		public int compare(Border o1, Border o2) {
			if (o1 == null) return o2 == null ? 0 : -1;
			else if (o2 == null) return 1;
			else if (o1.internalID > o2.internalID) return 1;
			else if (o1.internalID == o2.internalID) return 0;
			else return -1;
		}

		@Override
		public int compareTo(Border o) {
			if (o == null) return 1;
			else if (internalID == o.internalID) return 0;
			else if (internalID > o.internalID) return 1;
			else return -1;
		}
	}

	public static class NumberFormat implements Comparator<NumberFormat>, Comparable<NumberFormat> {
		public static final int CUSTOMFORMAT_START_NUMBER = 124;
		public static final int FORMAT_NONE = 0; // No format Default
		public static final int FORMAT_1 = 1; // Format: 0
		public static final int FORMAT_2 = 2; // Format: 0.00
		public static final int FORMAT_3 = 3; // Format: #,##0
		public static final int FORMAT_4 = 4; // Format: #,##0.00
		public static final int FORMAT_5 = 5; // Format: $#,##0_);($#,##0)
		public static final int FORMAT_6 = 6; // Format: $#,##0_);[Red]($#,##0)
		public static final int FORMAT_7 = 7; // Format: $#,##0.00_);($#,##0.00)
		public static final int FORMAT_8 = 8; // Format:
												// $#,##0.00_);[Red]($#,##0.00)
		public static final int FORMAT_9 = 9; // Format: 0%
		public static final int FORMAT_10 = 10; // Format: 0.00%
		public static final int FORMAT_11 = 11; // Format: 0.00E+00
		public static final int FORMAT_12 = 12; // Format: # ?/?
		public static final int FORMAT_13 = 13; // Format: # ??/??
		public static final int FORMAT_14 = 14; // Format: m/d/yyyy
		public static final int FORMAT_15 = 15; // Format: d-mmm-yy
		public static final int FORMAT_16 = 16; // Format: d-mmm
		public static final int FORMAT_17 = 17; // Format: mmm-yy
		public static final int FORMAT_18 = 18; // Format: mm AM/PM
		public static final int FORMAT_19 = 19; // Format: h:mm:ss AM/PM
		public static final int FORMAT_20 = 20; // Format: h:mm
		public static final int FORMAT_21 = 21; // Format: h:mm:ss
		public static final int FORMAT_22 = 22; // Format: m/d/yyyy h:mm
		public static final int FORMAT_37 = 37; // Format: #,##0_);(#,##0)
		public static final int FORMAT_38 = 38; // Format: #,##0_);[Red](#,##0)
		public static final int FORMAT_39 = 39; // Format: #,##0.00_);(#,##0.00)
		public static final int FORMAT_40 = 40; // Format:
												// #,##0.00_);[Red](#,##0.00)
		public static final int FORMAT_45 = 45; // Format: mm:ss
		public static final int FORMAT_46 = 46; // Format: [h]:mm:ss
		public static final int FORMAT_47 = 47; // Format: mm:ss.0
		public static final int FORMAT_48 = 48; // Format: ##0.0E+0
		public static final int FORMAT_49 = 49; // Format: #
		public static final int FORMAT_CUSTOM = 164; // Format custom 164

		public int number;
		public int customFormatID;
		public String customFormatCode;
		public int internalID;

		public boolean isCustomFormat() {
			return (number == FORMAT_CUSTOM);
		}

		public NumberFormat() {
			number = FORMAT_NONE;
			customFormatCode = "";
			customFormatID = CUSTOMFORMAT_START_NUMBER;
		}

		public boolean equals(NumberFormat other) {
			return other != null
					&& customFormatCode == other.customFormatCode
					&& customFormatID == other.customFormatID
					&& number == other.number;
		}

		public NumberFormat copy() {
			NumberFormat ret = new NumberFormat();
			ret.customFormatCode = customFormatCode;
			ret.customFormatID = customFormatID;
			ret.number = number;
			return ret;
		}

		@Override
		public int compare(NumberFormat o1, NumberFormat o2) {
			if (o1 == null) return o2 == null ? 0 : -1;
			else if (o2 == null) return 1;
			else if (o1.internalID > o2.internalID) return 1;
			else if (o1.internalID == o2.internalID) return 0;
			else return -1;
		}

		@Override
		public int compareTo(NumberFormat o) {
			if (o == null) return 1;
			else if (internalID == o.internalID) return 0;
			else if (internalID > o.internalID) return 1;
			else return -1;
		}

	}

	public static class BasicStyles {
		private enum StyleEnum {
			bold, italic, boldItalic, underline, doubleUnderlien, 
			strike, dateFormat, roundFormat, borderFrame, 
			borderFrameHeader, dottedFill_0_125, mergeCellStyle
		}

		private static Style bold, italic, boldItalic, underline,
				doubleUnderline, strike, dateFormat, roundFormat, borderFrame,
				borderFrameHeader, dottedFill_0_125, mergeCellStyle;

		public static Style getBold() {
			return getStyle(StyleEnum.bold);
		}

		public static Style getItalic() {
			return getStyle(StyleEnum.italic);
		}

		public static Style getBoldItalic() {
			return getStyle(StyleEnum.boldItalic);
		}

		public static Style getUnderline() {
			return getStyle(StyleEnum.underline);
		}

		public static Style getDoubleUnderline() {
			return getStyle(StyleEnum.doubleUnderlien);
		}

		public static Style getStrike() {
			return getStyle(StyleEnum.strike);
		}

		public static Style getDateFormat() {
			return getStyle(StyleEnum.dateFormat);
		}

		public static Style getRoundFormat() {
			return getStyle(StyleEnum.roundFormat);
		}

		public static Style getBorderFrame() {
			return getStyle(StyleEnum.borderFrame);
		}

		public static Style getBorderFrameHeader() {
			return getStyle(StyleEnum.borderFrameHeader);
		}

		public static Style getDottedFill_0_125() {
			return getStyle(StyleEnum.dottedFill_0_125);
		}

		public static Style getMergeCellStyle() {
			return getStyle(StyleEnum.mergeCellStyle);
		}

		private static Style getStyle(StyleEnum value) {
			Style s = null;
			switch (value) {
			case bold:
				if (bold == null) {
					bold = new Style();
					bold.currentFont.bold = true;
				}
				s = bold;
				break;
			case italic:
				if (italic == null) {
					italic = new Style();
					italic.currentFont.italic = true;
				}
				s = italic;
				break;
			case boldItalic:
				if (boldItalic == null) {
					boldItalic = new Style();
					boldItalic.currentFont.italic = true;
					boldItalic.currentFont.bold = true;
				}
				s = boldItalic;
				break;
			case underline:
				if (underline == null) {
					underline = new Style();
					underline.currentFont.underline = true;
				}
				s = underline;
				break;
			case doubleUnderlien:
				if (doubleUnderline == null) {
					doubleUnderline = new Style();
					doubleUnderline.currentFont.doubleUnderline = true;
				}
				s = doubleUnderline;
				break;
			case strike:
				if (strike == null) {
					strike = new Style();
					strike.currentFont.strike = true;
				}
				s = strike;
				break;
			case dateFormat:
				if (dateFormat == null) {
					dateFormat = new Style();
					dateFormat.currentNumberFormat.number = NumberFormat.FORMAT_14;
				}
				s = dateFormat;
				break;
			case roundFormat:
				if (roundFormat == null) {
					roundFormat = new Style();
					roundFormat.currentNumberFormat.number = NumberFormat.FORMAT_1;
				}
				s = roundFormat;
				break;
			case borderFrame:
				if (borderFrame == null) {
					borderFrame = new Style();
					borderFrame.currentBorder.topStyle = Border.StyleValue.thin;
					borderFrame.currentBorder.bottomStyle = Border.StyleValue.thin;
					borderFrame.currentBorder.leftStyle = Border.StyleValue.thin;
					borderFrame.currentBorder.rightStyle = Border.StyleValue.thin;
				}
				s = borderFrame;
				break;
			case borderFrameHeader:
				if (borderFrameHeader == null) {
					borderFrameHeader = new Style();
					borderFrameHeader.currentBorder.topStyle = Border.StyleValue.thin;
					borderFrameHeader.currentBorder.bottomStyle = Border.StyleValue.medium;
					borderFrameHeader.currentBorder.leftStyle = Border.StyleValue.thin;
					borderFrameHeader.currentBorder.rightStyle = Border.StyleValue.thin;
					borderFrameHeader.currentFont.bold = true;
				}
				s = borderFrameHeader;
				break;
			case dottedFill_0_125:
				if (dottedFill_0_125 == null) {
					dottedFill_0_125 = new Style();
					dottedFill_0_125.currentFill.patternFill = Fill.PatternValue.gray125;
				}
				s = dottedFill_0_125;
				break;
			case mergeCellStyle:
				if (mergeCellStyle == null) {
					mergeCellStyle = new Style();
					mergeCellStyle.currentCellXf.forceApplyAlignment = true;
				}
				s = mergeCellStyle;
				break;
			}
			return s;
		}
	}

	protected static <T> boolean isEquals(T src, T dst) {
		if (src == null) return dst == null;
		else return src.equals(dst);
	}

	public boolean equals(Style other) {
		return currentBorder.equals(other.currentBorder)
				&& currentFill.equals(other.currentFill)
				&& currentFont.equals(other.currentFont)
				&& currentNumberFormat.equals(other.currentNumberFormat)
				&& currentCellXf.equals(other.currentCellXf);
	}

	public Style copy() {
		return copy(currentNumberFormat.copy());
	}

	public Style copy(NumberFormat overwriteFormat) {
		Style ret = new Style(name + "_copy");
		ret.currentBorder = currentBorder.copy();
		ret.currentFill = currentFill.copy();
		ret.currentFont = currentFont.copy();
		ret.currentCellXf = currentCellXf.copy();
		ret.currentNumberFormat = overwriteFormat;
		return ret;
	}

	@Override
	public int compare(Style o1, Style o2) {
		if (o1 == null) return o2 == null ? 0 : -1;
		else if (o2 == null) return o1 == null ? 0 : 1;
		else if (o1.internalID == o2.internalID) return 0;
		else if (o1.internalID > o2.internalID) return 1;
		else return -1;
	}

	@Override
	public int compareTo(Style o) {
		if (o == null) return 1;
		else if (internalID == o.internalID) return 0;
		else if (internalID > o.internalID) return 1;
		else return -1;
	}

}
